<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />

    <title>CrmFetchKit.js Unit-Test</title>

    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/qunit/1.11.0/qunit.min.js"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/json2/20140204/json2.min.js"></script>

    <script type="text/javascript" src="alfa_CrmRestKit.js"></script>
    <script type="text/javascript" src="alfa_CrmFetchKit.js"></script>
    
    <script type="text/javascript" src="ClientGlobalContext.js.aspx"></script>

    <link type="text/css" media="screen" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/qunit/1.11.0/qunit.css" />
    
</head>
<body>
    <h1 id="qunit-header">
        QUnit: CrmFetchKit 1.10.0
    </h1>

    <h2 id="qunit-banner"></h2>

    <div id="qunit-testrunner-toolbar"></div>
    <h2 id="qunit-userAgent"></h2>
    <ol id="qunit-tests"></ol>
    <div id="qunit-fixture">
        test markup, will be hidden
    </div>

    <script type="text/javascript">

        /*globals CrmFetchKit, CrmRestKit, QUnit, $ */

        /*
        * QUnit notes:
        * 
        * var ctx = this; --> save the scope of the 'setup'  method for the CrmRestKit-callback fn. QUnit 
        * will invoke all setup, teardown, test and asyncTest method with the same scope (for each module)
        */
        
        /// 
        /// Error-handler for the CrmFetchKit
        ///
        function onFetchError(xhr, status, errorThrown) {

            debugger;
            var msg = $(xhr.responseXML).find('Message').text();

            // force fail
            QUnit.ok(false, 'gobal failded (this is really a bad thing): ' + msg);

            QUnit.start();
        }

        ///
        /// Error-handler for the CrmRestKit
        ///
        function onRestError(xhr, status, errorThrown) {

            debugger;
            var msg =  JSON.parse(xhr.responseText).error.message;

            // force fail
            QUnit.ok(false, 'gobal failded (this is really a bad thing): ' + msg);
            QUnit.start();
        }

        
        ///
        /// Setup: Executed before the very first test
        ///
        QUnit.begin(function () {
            
            // currently no inital setup required
        });

        ///
        /// Tear-down: Executed once all test are completed
        ///
        QUnit.done(function (failed, passed, total, runtime) {

            // no tear-down required
        });

        ///
        /// Party List /EntityCollection Property
        ///
        QUnit.module('Module: CrmFetchKit.Fetch - Access to the Entity Collection Property', {
            setup: function () {

                var ctx = this,
                    appointment = {
                        'Subject': 'Test Appointment - EnityCollection - Take 2',
                        ScheduledStart: new Date().toISOString(),
                        ScheduledEnd: new Date().toISOString()
                    };

                ctx.contactSet = [];
                ctx.numberOfAttendees = 4;
                ctx.requiredAttendees = [];

                // arrange create 4 contact records
                // while (ctx.contactSet.length < ctx.numberOfAttendees) {
                for(var i = 0; i < ctx.numberOfAttendees; i++) {

                    var contactData = {
                        LastName: 'Test-EntityCollection - ' + i
                    };

                    CrmRestKit.Create('Contact', contactData, false).then(function (data) {

                        ctx.contactSet.push(data.d.ContactId);

                        // create activity party records that reference the new created contact
                        var activityParty = {
                            PartyId: { Id: data.d.ContactId, LogicalName: 'contact' },
                            // Set the participation type (what role the party has on the activity).
                            // 5 --> 'Specifies a required attendee.' (http://msdn.microsoft.com/en-us/library/gg328549.aspx)
                            ParticipationTypeMask: { Value: 5 }
                        };

                        ctx.requiredAttendees.push(activityParty);

                    }, onRestError);
                }

                // add the parties to the "appointment_activity_parties" property
                appointment.appointment_activity_parties = ctx.requiredAttendees;

                CrmRestKit.Create('Appointment', appointment, false)
                    .then(function (data) {

                        ctx.appointmentid = data.d.ActivityId;

                    }, onRestError)
            },
            teardown: function () { }
        });


        QUnit.asyncTest('Fetch should return the entity-collection for attributes of type "Part List"', function(){

            var ctx = this;

            // arrange - construct the fetchxml query
            var appointmentid = ctx.appointmentid,
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" mapping="logical">',
                            '  <entity name="appointment">',
                            '    <attribute name="subject" />',
                            '    <attribute name="requiredattendees" />',
                            '    <filter type="and">',
                            '      <condition attribute="activityid" operator="eq" value="' + appointmentid + '" />',
                            '    </filter>',
                            '  </entity>',
                            '</fetch>'].join('');

            // action - execute the request
            CrmFetchKit.Fetch(fetchxml).then(function (results) {
                
                var appointment = results[0],
                    attendees = appointment.getValue('requiredattendees')

                QUnit.ok(attendees !== null);
                QUnit.ok(attendees.entities.length === 4);

                QUnit.ok(attendees.entities[0].getValue('partyid') !== null);

                // continue with the other tests
                QUnit.start();

            }, onFetchError);
        });


        ///
        /// Assert: Fetch
        ///
        QUnit.module('Module: CrmFetchKit.Fetch', {

            setup: function () {

                var ctx = this;

                // this name is used across several tests
                ctx.accountName = 'fetch-unit-test';
                // get the id of the current user
                ctx.whoAmIId = Xrm.Page.context.getUserId().toString().replace(/[{}]/g, '').toLowerCase();

                var record = {
                    Name: ctx.accountName,
                    AccountCategoryCode: { Value: 1 },
                    DoNotFax: true,
                    DoNotEMail: false
                };

                // arrange: create dummy record
                CrmRestKit.Create('Account', record, false).then(function (data) {

                    // store the id in the current scope object
                    ctx.accountid = data.d.AccountId;
                });
            },
            teardown: function () {

                // delete all unit-testing records
                var ctx = this,
                    filter = "(Name eq '" + ctx.accountName + "')";

                CrmRestKit.ByQuery('Account', ['AccountId'], filter, false).then(function (data) {

                    $.each(data.d.results, function (index, account) {

                        // perform the delete operaton is sync-mode
                        CrmRestKit.Delete('Account', account.AccountId, false);
                    });

                }, onRestError);
            }
        });

        QUnit.asyncTest( 'Fetch account reord (async)', function () {

            var ctx = this,
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" mapping="logical">',
                            '  <entity name="account">',
                            '    <attribute name="name" />',
                            '    <attribute name="accountid" />',
                            '    <attribute name="accountcategorycode" />',
                            '    <attribute name="createdon" />',
                            '    <attribute name="createdby" />',
                            '    <attribute name="donotfax" />',
                            '    <attribute name="donotemail" />',
                            '    <filter type="and">',
                            '      <condition attribute="name" operator="eq" value="' + ctx.accountName + '" />',
                            '    </filter>',
                            '  </entity>',
                            '</fetch>'].join('');

            // Action: load the account with a certain name
            CrmFetchKit.Fetch(fetchxml).then(function (results) {
                
                var account = results[0];

                // assert - the queries found only one element
                QUnit.equal(results.length, 1);

                // assert - the first result item is a business-entity
                QUnit.ok( account.Id !== null );
                QUnit.ok( account.Id === ctx.accountid );
                QUnit.ok( account.attributes !== null );
                QUnit.ok( account.logicalName === 'account' );
                
                //assert - the query return the correct set of attributes
                QUnit.equal( account.getValue('accountid'), ctx.accountid);
                QUnit.equal( account.getValue( 'name' ), ctx.accountName );

                // assert - check the option-set value
                QUnit.equal( account.getValue( 'accountcategorycode' ), 1, 'Correct OptionSet-Value');

                // assert - verify the lookup attr. defines all properties
                QUnit.equal( account.getValue('createdby'), ctx.whoAmIId);
                QUnit.ok( account.getValue('createdby', 'name') != null);
                QUnit.ok( account.getValue( 'createdby', 'logicalName' ) != null );

                // assert - check the boolean attr.
                QUnit.ok( account.getValue('donotfax') === true );
                QUnit.ok( account.getValue('donotemail') === false);

                // assert - check the fetched date attribute
                QUnit.ok(account.getValue('createdon') !== null);
                QUnit.ok(Object.prototype.toString.call(account.getValue('createdon')) === '[object Date]');

                // continue with the other tests
                QUnit.start();

            }, onFetchError);
        });

        QUnit.asyncTest( 'Fetch linked data (async)', function () {

            var ctx = this;
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" mapping="logical">',
                            '  <entity name="account">',
                            '    <attribute name="name" />',
                            '    <attribute name="accountid" />',
                            '    <attribute name="createdon" />',
                            '    <attribute name="createdby" />',
                            '    <filter type="and">',
                            '      <condition attribute="name" operator="eq" value="' + ctx.accountName + '" />',
                            '    </filter>',
                            '    <link-entity name="businessunit" from="businessunitid" to="owningbusinessunit" link-type="inner" alias="bu">',
                            '       <attribute name="name" />',
                            '       <filter>',
                            '           <condition attribute="parentbusinessunitid" operator="null" />',
                            '       </filter>',
                            '   </link-entity>',
                            '  </entity>',
                            '</fetch>'].join( '' );

            // action - execute fetchxml with linked entity
            CrmFetchKit.Fetch( fetchxml ).then( function ( results ) {

                // assert - access attr of the linked-entity
                QUnit.ok( results[0].getValue( 'bu.name' ) !== null, 'Access linked-attributes' );

                // continue with the other tests
                QUnit.start();

            }, onFetchError );
        } );

        QUnit.test('Fetch account reord (sync)', function () {

            var ctx = this,
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" mapping="logical">',
                            '  <entity name="account">',
                            '    <attribute name="name" />',
                            '    <attribute name="accountid" />',
                            '    <attribute name="createdon" />',
                            '    <attribute name="createdby" />',
                            '    <filter type="and">',
                            '      <condition attribute="name" operator="eq" value="' + ctx.accountName + '" />',
                            '    </filter>',
                            '  </entity>',
                            '</fetch>'].join('');

            // action: load the account with a certain name
            CrmFetchKit.Fetch(fetchxml, false).then(function (results) {
                
                var account = results[0];

                // assert - the queries found only one element
                QUnit.equal(results.length, 1);

                // assert - the first result item is a business-entity
                QUnit.ok(account.Id !== null);
                QUnit.ok(account.Id === ctx.accountid);
                QUnit.ok(account.attributes !== null);
                QUnit.ok(account.logicalName === 'account');

                //assert - the query return the correct set of attributes
                QUnit.equal(account.getValue('accountid'), ctx.accountid);
                QUnit.equal(account.getValue('name'), ctx.accountName);

                // assert - verify the lookup attr. defines all properties
                QUnit.equal(account.getValue('createdby'), ctx.whoAmIId);
                QUnit.ok(account.getValue('createdby', 'name') != null);
                QUnit.ok(account.getValue('createdby', 'logicalName') != null);

                // assert - check the fetched date attribute
                QUnit.ok(account.getValue('createdon') !== null);
                QUnit.ok(Object.prototype.toString.call(account.getValue('createdon')) === '[object Date]');

            }, onFetchError);
        });

        QUnit.asyncTest( 'Fetch option-set "type" bugfix (aync)', function () {
            var ctx = this;
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" mapping="logical">',
                            '  <entity name="account">',
                            '    <attribute name="name" />',
                            '    <attribute name="preferredcontactmethodcode" />',
                            '    <filter type="and">',
                            '      <condition attribute="name" operator="eq" value="' + ctx.accountName + '" />',
                            '    </filter>',
                            '  </entity>',
                            '</fetch>'].join( '' );
            
            // action - execute fetchxml with linked entity
            CrmFetchKit.Fetch( fetchxml ).then( function ( results ) {

                // assert - access attr of the linked-entity
                QUnit.equal( results[0].getValue( 'preferredcontactmethodcode', 'type' ), 'a:OptionSetValue' );

                // continue with the other tests
                QUnit.start();

            }, onFetchError );
        } );

        ///
        /// Assert: FetchMore
        ///
        QUnit.module('Module: CrmFetchKit.FetchMore', {

            setup: function () {

                var ctx = this,
                    settings = {
                        createPromiseSet: [],
                        numberOfRecords: 20,
                        countLimit: 10,
                        testname: 'fetch-unit-test'
                    };

                // create a some of contact records
                for (var i = 0; i < settings.numberOfRecords; i++) {

                    // add the promise-object to the collection
                    settings.createPromiseSet.push(
                        CrmRestKit.Create('Contact', { LastName: settings.testname + ' ' + i })
                    );
                }

                // keep the settings for the tests
                ctx.settings = settings;
            },
            teardown: function () {

                var ctx = this,
                    filter = "startswith(LastName,'" + ctx.settings.testname + "')";

                CrmRestKit.ByQuery('Contact', ['ContactId'], filter, false).then(function (data) {

                    $.each(data.d.results, function (index, contect) {

                        CrmRestKit.Delete('Contact', contect.ContactId, false);
                    });

                }, onRestError);
            }
        });

        QUnit.asyncTest('Fetch-More contact records (async)', function () {

            var ctx = this,
                afterFetchMoreIsReached = false,
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" ',
                                    'mapping="logical" ',
                                    'returntotalrecordcount="true" ',
                                    'count="' + ctx.settings.countLimit + '">',
                            '  <entity name="contact">',
                            '    <attribute name="lastname" />',
                            '    <attribute name="contactid" />',
                            '    <filter type="and">',
                            '      <condition attribute="lastname" operator="like" value="' + ctx.settings.testname + '%" />',
                            '    </filter>',
                            '  </entity>',
                            '</fetch>'].join('');
            
            // wait until all dummy contacts are created (invoke when with an array of promises)
            $.when.apply($, ctx.settings.createPromiseSet).then(function () {

                CrmFetchKit.FetchMore(fetchxml).then(function (response) {

                    // assert: verify that the request is in sync-mode
                    QUnit.ok(afterFetchMoreIsReached === true, 'Is executed in async');

                    // assert: response object provides the correct information
                    QUnit.equal(response.entityName, 'contact');
                    QUnit.equal(response.moreRecords, true);

                    QUnit.equal(response.totalRecordCount, ctx.settings.numberOfRecords);
                    QUnit.equal(response.entities.length, ctx.settings.countLimit);

                    QUnit.ok(response.pagingCookie !== null);

                    // continue with the other tests
                    QUnit.start();

                }, onFetchError);

                afterFetchMoreIsReached = true;

            }, onRestError);
        });

        QUnit.asyncTest('Fetch-More contact records (sync)', function () {

            // note: test itself is asnyc because the test-data is create in async mode, but the fetchmore is invoked in sync-mode
            
            var ctx = this,
                afterFetchMoreIsReached = false,
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" ',
                                    'mapping="logical" ',
                                    'returntotalrecordcount="true" ',
                                    'count="' + ctx.settings.countLimit + '">',
                            '  <entity name="contact">',
                            '    <attribute name="lastname" />',
                            '    <attribute name="contactid" />',
                            '    <filter type="and">',
                            '      <condition attribute="lastname" operator="like" value="' + ctx.settings.testname + '%" />',
                            '    </filter>',
                            '  </entity>',
                            '</fetch>'].join('');

            // wait until all dummy contacts are created (invoke when with an array of promises)
            $.when.apply($, ctx.settings.createPromiseSet).then(function () {
                
                CrmFetchKit.FetchMore(fetchxml, false).then(function (response) {

                    // assert: verify that the request is in sync-mode
                    QUnit.equal(afterFetchMoreIsReached, false, 'Is executed in sync');

                    // assert: response object provides the correct information
                    QUnit.equal(response.entityName, 'contact');
                    QUnit.equal(response.moreRecords, true);

                    QUnit.equal(response.totalRecordCount, ctx.settings.numberOfRecords);
                    QUnit.equal(response.entities.length, ctx.settings.countLimit);

                    QUnit.ok(response.pagingCookie !== null);

                    // continue with the other tests
                    QUnit.start();

                }, onFetchError);

                afterFetchMoreIsReached = true;

            }, onRestError);
        });
       
        ///
        /// Assert: Fetch-All
        ///
        QUnit.module('Module: CrmFetchKit.FetchAll (this method supports only async execution)', {
            setup: function () {
                 
                var ctx = this,
                    settings = {
                        createPromiseSet : [],
                        testname: 'fetch-unit-test',
                        numberOfRecords: 20,
                        countLimit: 10
                    };

                // create a some of contact records
                for (var i = 0; i < settings.numberOfRecords; i++) {

                    // add the promise-object to the collection
                    settings.createPromiseSet.push(
                        CrmRestKit.Create('Contact', { LastName: settings.testname + ' ' + i })
                    );
                }

                // keep the settings for the tests
                ctx.settings = settings;
            },
            teardown: function() {
                
                var ctx = this,
                    filter = "startswith(LastName,'" + ctx.settings.testname + "')";

                CrmRestKit.ByQuery('Contact', ['ContactId'], filter, false).then(function (data) {

                    $.each(data.d.results, function (index, contect) {

                        CrmRestKit.Delete('Contact', contect.ContactId);
                    });

                }, onRestError);
            }
        });

        QUnit.asyncTest('Fetch-All contact records (async)', function () {

            var ctx = this,
                afterFetchMoreIsReached = false,
                fetchxml = ['<fetch version="1.0" output-format="xml-platform" ',
                                    'mapping="logical" ',
                                    'count="' + ctx.settings.countLimit + '">',
                            '  <entity name="contact">',
                            '    <attribute name="lastname" />',
                            '    <attribute name="contactid" />',
                            '    <filter type="and">',
                            '      <condition attribute="lastname" operator="like" value="' + ctx.settings.testname + '%" />',
                            '    </filter>',
                            '  </entity>',
                            '</fetch>'].join('');

            // wait until all dummy contacts are created (invoke when with an array of promises)
            $.when.apply($, ctx.settings.createPromiseSet).then(function () {

                CrmFetchKit.FetchAll(fetchxml).then(function (entities) {

                    // assert: verify that the request is in sync-mode
                    QUnit.ok(afterFetchMoreIsReached === true, 'Is executed in async');

                    // assert: all records are loaded
                    QUnit.equal(entities.length, ctx.settings.numberOfRecords);

                    // continue with the other tests
                    QUnit.start();

                }, onFetchError);

                afterFetchMoreIsReached = true;

            }, onRestError);
        });

    </script>
</body>
</html>
